"""
$Id: central.py 2 2007-03-04 19:08:44Z sidnei.da.silva $
"""

import logging
import socket
import threading
from voting import constants


class Central(threading.Thread):

    def __init__(self, valid=None, address=('', constants.DEFAULT_PORT),
                 backlog=1):
        threading.Thread.__init__(self)
        assert valid is not None, 'List of valid electors is required'
        self.valid = dict([(zone_id, list(zone_data))
                           for zone_id, zone_data in valid.items()])
        self.votes = {}
        self.address = address
        self.backlog = backlog
        self.shutdown = False
        self.cond = threading.Condition()
        self.setDaemon(True)
        self.logger = logging.getLogger('voting.central')

    def run(self):
        try:
            try:
                # Initialize sockets, etc.
                self.setup()
                while True:
                    # Service a request. If it returns None the data
                    # received was not valid.
                    got = None
                    try:
                        got = self.service_one()
                    except:
                        self.logger.exception('Exception servicing request')

                    if got is None:
                        continue
                    # Handle received data.
                    self.handle(got)
                    # Break out when finished.
                    if self.finished():
                        break
            except:
                self.logger.exception('Exception in Central.')
        finally:
            self.logger.debug('Stopping %s', self.address)
            self.logger.info('Finished')

    def setup(self):
        # It's a TCP Socket
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # Bind to configured port on all interfaces
        self.sock.bind(self.address)
        # Service ``self.backlog`` requests at a time.
        self.sock.listen(self.backlog)
        self.logger.info('Started')
        self.logger.debug('Listening on %s', self.address)

        # Notify condition
        self.cond.acquire()
        self.cond.notify()
        self.cond.release()

    def service_one(self):
        self.logger.debug('Accepting connections...')
        conn, (host, port) = self.sock.accept()
        self.logger.debug('Connection from %s:%s', host, port)

        try:
            # Send ``ACCEPT`` to notify the client we are ready to start
            # receiving data.
            conn.send('ACCEPT\n')
            conn.shutdown(1)

            data = []
            while True:
                buf = conn.recv(constants.BUFSIZE)
                if not buf:
                    break
                data.append(buf)
            data = ''.join(data)

            return data
        finally:
            self.logger.debug('Closed connection from %s:%s', host, port)
            conn.close()

    def handle(self, data):
        zone_id, elector, vote = data.split(':')
        zone_id = int(zone_id.split('zone-')[-1])
        zone_data = self.valid.get(zone_id, None)
        if zone_data is None:
            self.logger.debug('Not a valid zone identifier (%s)', zone_id)
            return
        if not elector in zone_data:
            self.logger.debug('Not a valid elector (%s) for zone (%s)',
                              elector, zone_id)
            return
        vote = int(vote)
        if vote not in constants.VALID_CHOICES:
            self.logger.debug('Not a valid vote (%s)', vote)
            return
        self.logger.debug('Received vote (%s) from (zone-%s:%s)',
                          vote, zone_id, elector)

        self.cond.acquire()
        try:
            if not self.votes.has_key(vote):
                self.votes[vote] = 1
            else:
                self.votes[vote] += 1
            zone_data.remove(elector)
            self.cond.notify()
        finally:
            self.cond.release()

    def stop(self):
        if self.shutdown:
            self.logger.debug('Already finished')
            return
        self.logger.info('Shutdown requested')
        self.cond.acquire()
        try:
            self.shutdown = True
            self.cond.notify()
        finally:
            self.cond.release()

    def finished(self):
        if self.shutdown:
            return True
        for zone_id, valid in self.valid.items():
            if valid:
                return False
        self.logger.debug('All valid votes received!')
        self.shutdown = True
        return True

if __name__ == '__main__':
    logging.basicConfig(level=logging.DEBUG)
    t = Central({'zone-1':['1', '2']})
    t.start()
    raw_input('Press a key to exit...')
    t.stop()
